package com.chen.cloudpicture.manager.sharding;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

/**
 * 图片分表算法实现类
 *
 * 旗舰款团队空间的图片数据单独建表管理。建表名称：picture_${spaceId}
 * 分库分表有水平分表也有垂直分表。垂直分表就是拆分表结构，水平分表就是把不同数据放到不同表
 * 我们可以根据数据分片规则对数据进行水平分表，（将数据按一定规则放到多个库表中，如取模，范围取值等等）
 * ShardingSphere 是成熟的分库分表框架，也分为ShardingSphere-JDBC 和 ShardingSphere-Proxy。
 * 前者嵌入代码，仅支持 java 语言，后者是一个独立的中间件代理(类似 Nginx)，支持多语言，大型分布式系统。个人用就用前者就行。
 * 分库分表策略又有静态分表和动态分表。
 * 静态分表分表的数量和规则都是固定的，一般就按照某个字段取模或者某一范围就分到了某个表；直接在配置文件写就行，简单但不利于扩展；
 * 动态分表分表的数量在运行时会根据业务或数据数量动态增加，更灵活，扩展性强。首先要自定义分表算法类，还要在代码中编写动态创建表的逻辑。
 */
public class PictureShardingAlgorithm implements StandardShardingAlgorithm<Long> {

    /**
     * 图片分表算法
     * 建表和查询都会走这个方法判断，查询的时候判断走哪张表，未指定 spaceId 会自动联表查询所有 Picture 表，
     * 这样查询时间会增加性能下降，所以不能分太多表，只有对重要客户才能分表。
     * 但是在上传图片时不指定 spaceId，系统就不知道应该把图片插入到哪张 Picture 表中，就会报错，
     * 所以所有增删改查操作都必须指定 spaceId，约定公共空间 spaceId 为 0，才能避免报错和多表查询影响效率
     * （上条这里我其实没做到，因为要重构的地方太多了，所以暂时关闭了分库分表，经测试分库分表是可以用的）
     * 经过开发和测试，动态分库分表的实现非常麻烦。某些单表的查询性能是高了，但整体查询的性能可能会减少，
     * 所以原则上 非必要不分表，一定要找到合适的应用场景。
     *
     * @param availableTargetNames 所有支持的分表名(包括创建和查询，查询的时候框架会自动查询对应分表不用在代码中指定)，在配置文件指定
     * @param preciseShardingValue 分表字段，按照配置文件这里就是 spaceId
     * @return
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
        Long spaceId = preciseShardingValue.getValue();
        String logicTableName = preciseShardingValue.getLogicTableName(); // 逻辑表名(picture)
        // spaceId 为 null 表示查询所有图片
        if (spaceId == null) {
            return logicTableName;
        }
        // 根据 spaceId 动态生成分表名
        String realTableName = "picture_" + spaceId;
        if (availableTargetNames.contains(realTableName)) {
            return realTableName;
        } else {
            return logicTableName;
        }
    }

    // 按范围分表，我们这个项目没用到，包括下面的方法都无需实现
    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {
        return new ArrayList<>();
    }

    @Override
    public Properties getProps() {
        return null;
    }

    @Override
    public void init(Properties properties) {

    }
}
